/*
 * Copyright (c) 2019, NULL-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-08-26     sogwms       The first version
 */

#include "ab_phase_encoder.h"

static rvm_dev_t *gpio_a = NULL;
static rvm_dev_t *gpio_b = NULL;

struct ab_phase_encoder
{
    struct encoder  enc;
    rvm_dev_t       pin_a;            /* interrupt pin  */
    rvm_dev_t       pin_b;
    int16_t         last_value;
};

static const unsigned char  AB_PHASE_ENCODER_TABLE[4] = {2, 0, 3, 1};

static void encoder_isr(rvm_dev_t *dev, void *arg)
{
    rvm_hal_gpio_pin_data_t value[2];
    ab_phase_encoder_t enc_sub = (ab_phase_encoder_t)arg;

    rvm_hal_gpio_pin_read(gpio_a, &value[0]);
    rvm_hal_gpio_pin_read(gpio_b, &value[1]);
    
    int val = value[0] * 2 + value[1];

    if (AB_PHASE_ENCODER_TABLE[enc_sub->last_value] == val)
    {
        enc_sub->enc.dir = ENCODER_DIR_FORWARD;
        enc_sub->enc.pulse_count++;
    }
    else
    {
        enc_sub->enc.dir = ENCODER_DIR_BACKWARD;
        enc_sub->enc.pulse_count--;
    }
    enc_sub->last_value = val;
}
#include <soc.h>
static signed long ab_phase_encoder_enable(void *enc)
{
    rvm_hal_gpio_pin_data_t val_a, val_b;
    ab_phase_encoder_t enc_sub = (ab_phase_encoder_t)enc;

    // Attach interrupts
    rvm_hal_gpio_pin_set_direction(gpio_a, RVM_GPIO_DIRECTION_OUTPUT);
    rvm_hal_gpio_pin_set_direction(gpio_b, RVM_GPIO_DIRECTION_OUTPUT);

    rvm_hal_gpio_pin_set_irq_mode(gpio_a, RVM_GPIO_IRQ_MODE_FALLING_EDGE);
    rvm_hal_gpio_pin_set_irq_mode(gpio_b, RVM_GPIO_IRQ_MODE_FALLING_EDGE);

    rvm_hal_gpio_pin_attach_callback(gpio_a, encoder_isr, gpio_a);
    rvm_hal_gpio_pin_attach_callback(gpio_b, encoder_isr, gpio_b);

    rvm_hal_gpio_pin_read(gpio_a, &val_a);
    rvm_hal_gpio_pin_read(gpio_b, &val_b);

    enc_sub->last_value = val_a * 2 + val_b;
    enc_sub->enc.last_time = aos_sys_tick_get();
    rvm_hal_gpio_pin_irq_enable(gpio_a, 1);
    rvm_hal_gpio_pin_irq_enable(gpio_b, 1);


    return 0;
}

static signed long ab_phase_encoder_disable(void *enc)
{
    ab_phase_encoder_t enc_sub = (ab_phase_encoder_t)enc;

    rvm_hal_gpio_pin_irq_enable(gpio_a, 0);
    rvm_hal_gpio_pin_irq_enable(gpio_b, 0);

    return 0;
}

static signed long ab_phase_encoder_destroy(void *enc)
{
    ab_phase_encoder_disable(enc);
    ab_phase_encoder_t enc_sub = (ab_phase_encoder_t)enc;

    rvm_hal_gpio_pin_irq_enable(gpio_a, 0);
    rvm_hal_gpio_pin_irq_enable(gpio_b, 0);

    free(enc_sub);

    return 0;
}

ab_phase_encoder_t ab_phase_encoder_create(signed long pin_a, signed long pin_b, unsigned short pulse_revol, unsigned short sample_time)
{
    char filename[16];
    // Malloc memory and initialize pulse_count and pin
    ab_phase_encoder_t new_encoder = (ab_phase_encoder_t)encoder_create(sizeof(struct ab_phase_encoder), sample_time);
    if(new_encoder == NULL)
    {
        return NULL;
    }

    rvm_gpio_pin_drv_register(pin_a);
    rvm_gpio_pin_drv_register(pin_b);

    sprintf(filename, "gpio_pin%d", (int)pin_a);
    gpio_a = rvm_hal_gpio_pin_open(filename);

    sprintf(filename, "gpio_pin%d", (int)pin_b);
    gpio_b = rvm_hal_gpio_pin_open(filename);

    new_encoder->last_value = 0;
    new_encoder->enc.pulse_revol = pulse_revol;
    new_encoder->enc.enable = ab_phase_encoder_enable;
    new_encoder->enc.disable = ab_phase_encoder_disable;
    new_encoder->enc.destroy = ab_phase_encoder_destroy;

    return new_encoder;
}
